/*
   Copyright 2006 Gavin Bong

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/
package com.raverun;

import java.util.Vector;

import javax.microedition.lcdui.Canvas;
import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.Font;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.CommandListener;

public class ListContainer extends Canvas implements CommandListener {
    
    public Object syncObject = new Object();
    
    private UIController m_uiController;    

    // Todo put this into the resource file
    public final static Command SELECT_COMMAND = new Command("Select", Command.SCREEN, 2);

    public final int canvasWidth;
    public final int canvasHeight;
    
    //START: ListElement related dimensions
    public final int HIGHLIGHT_XOFFSET;
    public final int HIGHLIGHT_WIDTH;
    public final int HIGHLIGHT_YOFFSET;
    //  END: ListElement related dimensions
    
    private Vector m_elements = new Vector();

    private int m_firstVisibleLine;
    private int m_lastVisibleLine;

    private int numOfPages;
    
    private int m_maxVisibleLines;

    private int XOFFSET;

    private int YOFFSET;

    private int currentSelectedElement = 0;
    
    // Vertical padding between each ListElement
    private static final int LINE_PADDING = 2;

    private Font m_selectedFont = Font.getFont(Font.FACE_MONOSPACE, Font.STYLE_BOLD, Font.SIZE_MEDIUM);;

    private Font m_normalFont = Font.getFont(Font.FACE_MONOSPACE, Font.STYLE_PLAIN, Font.SIZE_MEDIUM);

    private boolean m_init;
    
    public final Shared m_shared;
    
    public ListContainer( UIController uiController, String title, Shared shared) {
                
        setTitle( title );
        
        m_uiController = uiController;
        
        canvasWidth = getWidth();
        canvasHeight = getHeight();
        XOFFSET = normalize( canvasWidth, 5 );
        YOFFSET = normalize( canvasHeight, 8 );

        HIGHLIGHT_XOFFSET = sizing( canvasHeight );
        HIGHLIGHT_YOFFSET = HIGHLIGHT_XOFFSET;
        HIGHLIGHT_WIDTH = canvasWidth-normalize( canvasWidth, 9 );
                
        _("w=" + canvasWidth + " | h=" + canvasHeight + " | XOFFSET=" + XOFFSET + " | YOFFSET=" + YOFFSET);
        _("HIGHLIGHT: xoffset=" + HIGHLIGHT_XOFFSET + " | width=" + HIGHLIGHT_WIDTH);

        m_shared = shared;
        
        addCommand( ListContainer.SELECT_COMMAND );
        setCommandListener(this);
    }

    /**
     * <ul>
     * <li>small &lt; 130
     * <li>medium &lt; 200
     * <li>large &gt; 200
     * </ul> 
     *
     * @param dimension either height or width
     * @return value to represent "small", "medium" or "large" screens
     */
    public int sizing( int dimension )
    {        
        if ( dimension >= 60 && dimension <= 130 )
        {
            return 2; // small
        }
        else if ( dimension >= 131 && dimension <= 200 )
        {
            return 3; // medium
        }
        else if ( dimension >= 201 )
        {
            return 4; // large
        }
        else
            return 0; // undefined
    }
    
    public static int normalize( int dimension, int percentage )
    {
        _("\t\tdimension=" + dimension);
        int t = percentage * dimension;
        return (t/100);        
    }    
    
    /**
     * ThreadSafe: only called once during construction
     *
     * Sets up the Viewport
     */
    public void init()
    {
        m_firstVisibleLine = 0;        
        m_lastVisibleLine = (getNumberOfLines() == 0) ? 0 : calcMaximumVisibleLines() - 1;                        
        
        m_init = true;
    }    

    public void resetList()
    {
        if (m_elements != null)
            m_elements.removeAllElements();
        else
            m_elements = new Vector();
    }
    
     /**
     * Gets the number of elements in the List.
     */
    public int size()
    {
        if (m_elements == null)
            return 0;
        return m_elements.size();
    }        
    
    /**
     * Thread safe? no
     */
    public void viewportUp()
    {
        m_firstVisibleLine--;
        m_lastVisibleLine--;
        _("\tviewport UP: firstVisibleLine=" + m_firstVisibleLine);
        _("\tviewport UP:  lastVisibleLine=" + m_lastVisibleLine);
    }

    /**
     * Thread safe? no
     */    
    public void viewportDown()
    {
        m_firstVisibleLine++;
        m_lastVisibleLine++;        
        _("\tviewport DOWN: firstVisibleLine=" + m_firstVisibleLine);
        _("\tviewport DOWN:  lastVisibleLine=" + m_lastVisibleLine);
    }
    
    public void keyPressed(int keyCode) 
    {
        _("keyCode = " + keyCode);                
        switch( getGameAction(keyCode) )
        {    
            case FIRE:
                _("Fire");
                commandAction( ListContainer.SELECT_COMMAND, this );
                break;
            case UP:
                _("Going: UP");
                moveUp();
                repaint();
                break;
            case DOWN:
                _("Going: DOWN");
                moveDown();
                repaint();
                break;
            default:
                break;
        }
    }
    
    private void moveUp() 
    {
        synchronized (syncObject)
        {
            if (currentSelectedElement > 0)
            {
                currentSelectedElement--;
            }

            if ( (currentSelectedElement < m_firstVisibleLine) && m_firstVisibleLine > 0)
            {
                viewportUp();
            }
        }
    }    
    
    private void moveDown() {
        int numberOfLines = getNumberOfLines();        
        
        synchronized (syncObject)
        {        
            if (numberOfLines > 1)
            {
                if (currentSelectedElement < numberOfLines-1)
                {
                    _("\tpointer = " + currentSelectedElement);
                    currentSelectedElement++;
                }
            }        

            if ( m_firstVisibleLine == (numberOfLines-m_maxVisibleLines+1) )
                return;        

            if (currentSelectedElement > m_lastVisibleLine)
            {
                viewportDown();
            }
        }
    }    

    /**
     * Uses globals:
     * <ul>
     * <li>currentSelectedElement
     * <li>maxVisibleLines
     * </ul>
     *
     * @deprecated this is not used at all
     */
    private boolean canMoveDown()
    {
        return ( (currentSelectedElement % m_maxVisibleLines) == 0 );
    }
    
    private int calcNumberOfPages( final int pageSize, final int totalLines ) 
    {        
        if (numOfPages == 0)
        {
           numOfPages = (totalLines/pageSize) + (((totalLines % pageSize) != 0) ? 1 : 0);
        }
        return numOfPages;
    }        
    
    /**
     * Adds the ListElement as long as the <code>init</code> method has not been called 
     */
    public void append(ListElement element) {
        if (!m_init)
            m_elements.addElement( element );
    }

    public int calcIncrementSize() {
        return (getSelectedFont().getHeight() + LINE_PADDING + (2 * ListElement.VERTICAL_MARGIN));
    }

    protected void paint(Graphics g) {
        g.setColor(14, 14, 102);
        g.fillRect(0, 0, canvasWidth, canvasHeight);

        if (m_elements.isEmpty())
            return;

        calcMaximumVisibleLines();
        _("maxVisibleLines = " + m_maxVisibleLines);

        int numberOfLines = getNumberOfLines();

        ListElement anElement = null;
        int elementIndex = 0;        

        g.setColor(0, 0, 0);          
        g.setFont( getSelectedFont() );                       
//        synchronized (syncObject)
        {
            int invisibleLine = m_firstVisibleLine + m_maxVisibleLines;
            int firstInvisibleLine = (numberOfLines < invisibleLine) ? numberOfLines
                    : invisibleLine;

            _("\tincrementsize = " + calcIncrementSize());        
            for (int i = m_firstVisibleLine; i < firstInvisibleLine; i++, elementIndex++) {         
                anElement = (ListElement) m_elements.elementAt(i);
                anElement.paint(g, XOFFSET, YOFFSET + (elementIndex * calcIncrementSize()), ( currentSelectedElement == i ));
            }
        }
    }
        
    private boolean isScrollable()
    {
        return getNumberOfLines() > m_maxVisibleLines;
    }    
    
    private int calcMaximumVisibleLines() {
        if (m_maxVisibleLines == 0) {
            m_maxVisibleLines = ((canvasHeight - YOFFSET) / calcIncrementSize());
        }
        return m_maxVisibleLines;
    }

    public Font getSelectedFont() {
        return m_selectedFont;
    }

    private final int getNumberOfLines() {
        if (m_elements == null || m_elements.isEmpty())
            return 0;
        return m_elements.size();
    }

    public static void _(String s) {
        System.out.println(s);
    }

    public void commandAction(Command command, Displayable arg1) {
        if (command == ListContainer.SELECT_COMMAND)
        {
            if (getNumberOfLines() > 0)
            {
                ListElement e = (ListElement)m_elements.elementAt( currentSelectedElement );
                _(e.m_label + ": selected");
            }
        }
        else if( command == m_uiController.mExitCommand )
        {
            m_uiController.exitRequested();
        }            
    }
    
    public void updateList( Runnable r )
    {
        m_uiController.updateList( r );
    }
}
